/**
 * @file stack.c
 * @author your name (you@domain.com)
 * @brief stack
 * @version 0.1
 * @date 2023-08-07
 * 
 * @copyright Copyright (c) 2023
 * 
 */
/******************************* Includes ********************************/
#include "stack.h"
#include <stdlib.h>
#include <string.h>

/******************************* Macros ******************************/
#define ADDR_SIZE sizeof(void *)
/******************************* Typedef ******************************/

/******************************* Variables ******************************/

/******************************* Functions ******************************/
stack_t *stack_create(int max_count)
{
    stack_t *stack = malloc(sizeof(stack_t));
    if(stack == NULL)
    {
        STACK_E("stack malloc fail\n");
        return NULL;
    }
    
    stack->top = -1;
    stack->max_count = max_count;
    stack->data = malloc(ADDR_SIZE*max_count);
    if(stack->data == NULL)
    {
        STACK_E("data malloc fail\n");
        return NULL;
    }

    memset(stack->data, 0, sizeof(ADDR_SIZE*max_count));

    return stack;
}

void stack_destory(stack_t *stack)
{
    if(stack == NULL)
    {
        STACK_E("stack malloc fail\n");
        return;
    }

    free(stack->data);
    free(stack);
}

int stack_is_empty(stack_t *stack)
{
    if(stack == NULL)
    {
        STACK_E("stack is full");
        return 0;
    }
       
    return stack->top == -1;
}

int stack_is_full(stack_t *stack)
{
    if(stack == NULL)
    {
        STACK_E("stack is full");
        return 0;        
    }

    return stack->top == stack->max_count - 1;
}

void stack_push(stack_t *stack, void *data)
{
    if(stack_is_full(stack) && data != NULL)
    {
        STACK_E("stack is full");
        return;
    }
    
    ++stack->top;
    void *temp = stack->data + stack->top * ADDR_SIZE;
    memcpy(temp, &data, ADDR_SIZE);
}

void *stack_pop(stack_t *stack)
{
    if (stack_is_empty(stack)) 
    {
        printf("stack is empty. Cannot pop element.\n");
        return NULL;
    }
    
    void *value =  NULL;
    void *src_value = stack->data + stack->top * ADDR_SIZE;
    memcpy(&value, src_value, ADDR_SIZE);
    stack->top--;
    return value;
}

void stack_clear_all(stack_t *stack)
{
     while(1)
     {
        if(stack_pop(stack) == NULL)
        {
            return;
        }
     }
}

void *stack_peek(stack_t *stack)
{
    if (stack_is_empty(stack)) 
    {
        printf("stack is empty. cannot pop element.\n");
        return NULL;
    }

    void *value =  NULL;
    int temp = stack->top * ADDR_SIZE;
    memcpy(&value, stack->data + temp, ADDR_SIZE);
    return value;
}

#ifdef STACK_TEST
typedef struct test
{
   char data[100];
   int i;
}test_t;

int main(void)
{
    int i = 0;
    stack_t *handle = stack_create(100);
    while(!stack_is_full(handle))
    {
        test_t *a = malloc(sizeof(test_t));
        printf("index: %d %ld\n", i, a);
        a->i = i++;
        snprintf(a->data, sizeof(a->data), "%d", i);
        stack_push(handle, a);
    }

    test_t *p = stack_peek(handle);
    printf("peek value:%d %s %ld\n", p->i, p->data, p);

    while(!stack_is_empty(handle))
    {
        test_t *p = stack_pop(handle);
        printf("pop value:%d %s %ld\n", p->i, p->data, p);
    }

    stack_peek(handle);

    stack_destory(handle);
}
#endif
/******************************* End of file ******************************/